using System;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Clue_Less_Client.localhost;

namespace Clue_Less_Client
{
    public partial class MainForm : Form
    {
        private GameStatus _gameStatus = GameStatus.Waiting;
        private Rules _rules = null;
        private int _gameID;
        private Suggestion _incomingSuggestion;

        public int GameID
        {
            get { return _gameID; }
        }
        private Player _player = null;

        public Player Player
        {
            get { return _player; }
        }
        private MainService ws;

        public MainService WS
        {
            get { return ws; }
        }
        private Board _board;

        public Board Board
        {
            get { return _board; }
        }
        private ChatMessage[] _chats = new ChatMessage[0];
        private Character[] _characters = new Character[0];

        public bool IsMyTurn
        {
            get { return _player.Status == PlayerStatus.Active; }
        }

        public Character[] Characters
        {
            get { return _characters; }
        }

        public ChatMessage[] Chats
        {
            get { return _chats; }
        }

        public MainForm(Player player, int gameID)
        {
            try
            {
                ws = new MainService();
            }
            catch (Exception e)
            {
                Error.Show("There was en error connecting to Web Service. Please try again later.", e);
                this.Close();
            }
            _player = player;
            _gameID = gameID;
            _board = new Board(this);
            _rules = new Rules(_board);

            InitializeComponent();
            timerChat.Start();
            timerGame.Start();
            timerGame_Tick(this, null);
        }

        void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.Message);
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            int gameID = Properties.Settings.Default.GameID;
            int playerID = Properties.Settings.Default.PlayerID;
            Player player = new Player();
            try
            {
                localhost.MainService ws = new MainService();
                if (gameID == -1 ||
                    ws.GetGameStatus(gameID) == GameStatus.Finished)
                {
                    Login loginFrm = new Login();
                    player = loginFrm.ProcessLogin();
                    if (loginFrm != null && player != null)
                    {
                        gameID = loginFrm.GameID;
                        player = (Player)ws.GetCharacter(gameID, player.PlayerID);
                    }
                    else
                        return;
                }
                else
                {
                    player = (Player)ws.GetCharacter(gameID, playerID);
                }

                if (player != null)
                {
                    Application.Run(new MainForm(player, gameID));
                }
            }
            catch (Exception ex)
            {
                Error.Show(ex.Message, ex);
            }
        }

        /// <summary>
        /// Draws out players cards
        /// </summary>
        private void DrawCards()
        {
            for (int i = 0; i < _player.Cards.Length; i++)
            {
                Control ctrl = GetControlByName(this.panel3, "pictureBox" + (i + 1));
                if (ctrl != null)
                {
                    PictureBox box = (PictureBox)ctrl;
                    box.Tag = _player.Cards[i];
                    if (_player.Cards[i] is WeaponCard)
                        box.Image = Images.weaponCards[((WeaponCard)_player.Cards[i]).Value];

                    if (_player.Cards[i] is RoomCard)
                        box.Image = Images.roomCards[((RoomCard)_player.Cards[i]).Value];

                    if (_player.Cards[i] is CharacterCard)
                        box.Image = Images.characterCards[((CharacterCard)_player.Cards[i]).Value];

                    ToolTip1.SetToolTip(box, _player.Cards[i].Name);
                }

            }
            for (int i = 1; i <= 6; i++)
            {
                Control ctrl = GetControlByName(this.panel3, "pictureBox" + i);
                if (ctrl.Tag == null)
                    ctrl.Visible = false;
                else
                    ctrl.Visible = true;
            }
        }

        /// <summary>
        /// Draws out initial board
        /// </summary>
        private void DrawBoard()
        {
            _board.Draw();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            DrawBoard();
            DrawCards();
        }

        #region Helper functions
        /// <summary>
        /// Finds control by name
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public Control GetControlByName(Control parent, string name)
        {
            Control[] controls = parent.Controls.Find(name, true);
            if (controls.Length > 0)
                return controls[0];
            else
                return null;
        }
        #endregion

        #region Chat related functions
        private void btnSendMessage_Click(object sender, EventArgs e)
        {
            try
            {
                if (tbSendMessage.Text.Length > 0)
                {
                    ws.SendChatMessage(_gameID, _player.PlayerID, tbSendMessage.Text);
                    timerChat_Tick(this, null);
                }

                tbSendMessage.Text = "";
            }
            catch (Exception ex)
            {
                Error.Show("Failed to send chat message.", ex);
            }
        }

        private void tbSendMessage_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                btnSendMessage.PerformClick();
            }
        }
        #endregion

        #region Timers
        private void timerChat_Tick(object sender, EventArgs e)
        {
            try
            {
                if (_chats != null)
                {
                    ws.GetChatMessagesAsync(_gameID, Util.GetChatChecksum(_chats), Guid.NewGuid());
                    ws.GetChatMessagesCompleted += new GetChatMessagesCompletedEventHandler(ws_GetChatMessagesCompleted);
                }
            }
            catch (Exception ex)
            {
                Error.Show("There was an error connecting to the server. Timer failed.", ex);
            }
        }

        private void timerGame_Tick(object sender, EventArgs e)
        {
            try
            {
                if (_characters != null)
                {
                    ws.GetCharactersPlayingAsync(_gameID, Util.GetCharactersChecksum(_characters), Guid.NewGuid());
                    ws.GetCharactersPlayingCompleted += new GetCharactersPlayingCompletedEventHandler(ws_GetCharactersPlayingCompleted);
                    ws.GetCharacterAsync(_gameID, _player.PlayerID, Guid.NewGuid());
                    ws.GetCharacterCompleted += new GetCharacterCompletedEventHandler(ws_GetCharacterCompleted);
                    ws.GetGameStatusAsync(_gameID, Guid.NewGuid());
                    ws.GetGameStatusCompleted += new GetGameStatusCompletedEventHandler(ws_GetGameStatusCompleted);
                    ws.GetSuggestionAsync(_gameID, Guid.NewGuid());
                    ws.GetSuggestionCompleted += new GetSuggestionCompletedEventHandler(ws_GetSuggestionCompleted);
                }
            }
            catch (Exception ex)
            {
                Error.Show("There was an error connecting to the server. Timer failed.", ex);
            }
        }

        void ws_GetSuggestionCompleted(object sender, GetSuggestionCompletedEventArgs e)
        {
            if (e.Error == null &&
                e.Result != null)
            {

                // New suggestion
                _incomingSuggestion = e.Result;

                if (!_incomingSuggestion.IsDisproved
                    && _incomingSuggestion.PlayerID != _player.PlayerID
                    && _incomingSuggestion.DisproverID != _player.PlayerID)
                {
                    lblStatus.Text = _incomingSuggestion.PlayerName + " has made a suggestion: murder has happened in " + _incomingSuggestion.SuspectRoom.Name +
                        " with a " + _incomingSuggestion.SuspectWeapon.Name + " by " + _incomingSuggestion.SuspectCharacter.Name;
                }

                if (!_incomingSuggestion.IsDisproved
                    && _incomingSuggestion.PlayerID != _player.PlayerID
                    && _incomingSuggestion.DisproverID == _player.PlayerID)
                {
                    if (_rules.CanDisprove(_player, _incomingSuggestion))
                    {
                        lblStatus.Text = "Can you disprove this: murder has happened in " + _incomingSuggestion.SuspectRoom.Name +
                            " with a " + _incomingSuggestion.SuspectWeapon.Name + " by " + _incomingSuggestion.SuspectCharacter.Name + "?" + Environment.NewLine +
                            "To disprove, please select one of the cards from your deck...";
                        tsStatus.Text = "Please disprove.";
                    }
                    else
                    {
                        ws.Disprove(_gameID, _player.PlayerID, _incomingSuggestion.SuggestionID, true, null);
                    }
                }
            }
        }

        void ws_GetCharacterCompleted(object sender, GetCharacterCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                _player = (Player)e.Result;

                if (_gameStatus == GameStatus.Playing)
                {
                    if (IsMyTurn)
                    {
                        lblStatus.Text = _player.CharacterType.ToString() + " please make your move.";
                        tsStatus.Text = "Your turn.";
                        btnAccuse.Enabled = true;
                        if (!_rules.CanMove(_player, _characters)) 
                            btnSkip.Enabled = true;
                        else 
                            btnSkip.Enabled = false;

                        // Stop timer
                        //timerGame.Stop();
                    }
                    else
                    {
                        if (_incomingSuggestion == null ||
                            _incomingSuggestion.IsDisproved)
                        {
                            if (_player.Status != PlayerStatus.Disabled)
                            {
                                lblStatus.Text = "Waiting for other players to make their moves.";
                                tsStatus.Text = "Waiting...";
                            }
                            else
                            {
                                lblStatus.Text = "You have lost the game. You may now only observe and disprove.";
                            }
                        }
                        btnAccuse.Enabled = false;
                        btnSkip.Enabled = false;
                    }
                }
            }
        }

        void ws_GetGameStatusCompleted(object sender, GetGameStatusCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                _gameStatus = e.Result;
                if (_gameStatus == GameStatus.Finished &&
                    IsMyTurn)
                {
                    lblStatus.Text = "Congratulations! You have won!!!";
                }
            }
        }

        void ws_GetChatMessagesCompleted(object sender, GetChatMessagesCompletedEventArgs e)
        {
            if (e.Error == null && e.Result != null)
            {
                _chats = e.Result;
                _board.RefreshChat();
            }
        }

        void ws_GetCharactersPlayingCompleted(object sender, GetCharactersPlayingCompletedEventArgs e)
        {
            if (e.Error == null && e.Result != null)
            {
                _characters = e.Result;
                for (int i = 0; i < _characters.Length; i++)
                {
                    // Link player to array of characters
                    if (_characters[i].PlayerID == _player.PlayerID)
                    {
                        _player.XPosition = _characters[i].XPosition;
                        _player.YPosition = _characters[i].YPosition;
                        _player.Status = _characters[i].Status;
                        _characters[i] = _player;
                    }
                }
                _board.Draw();
            }
        }

        #endregion

        #region User Events
        private void pbCell_MouseHover(object sender, EventArgs e)
        {
            if (sender != null && sender is PictureBox)
            {
                PictureBox pb = (PictureBox)sender;
                BoardCell cell = (BoardCell)pb.Tag;
                
                if (IsMyTurn &&
                    _rules.CanMove(_player, cell, _characters))
                {
                    pb.Cursor = Cursors.Hand;
                    _board.DrawCell(cell, pb, true);
                }
                else
                {
                    pb.Cursor = Cursors.No;
                }
                if (String.IsNullOrEmpty(ToolTip1.GetToolTip(pb)))
                {
                    ToolTip1.SetToolTip(pb, cell.Room.ToString());
                }
            }
        }

        private void pbCell_MouseLeave(object sender, EventArgs e)
        {
            if (sender != null && sender is PictureBox)
            {
                PictureBox pb = (PictureBox)sender;
                BoardCell cell = (BoardCell)pb.Tag;
                _board.DrawCell(cell, pb, false);
            }
        }

        private void pbCell_Click(object sender, EventArgs e)
        {
            if (sender != null)
            {
                PictureBox pb = (PictureBox)sender;
                BoardCell cell = (BoardCell)pb.Tag;
                if (_rules.CanMove(_player, cell, _characters))
                {
                    if (IsMyTurn)
                    {
                        _player.XPosition = cell.XPosition;
                        _player.YPosition = cell.YPosition;
                        if (ws.MakeMove(_gameID, (Character)_player))
                        {
                            _board.Draw();
                            if (_rules.CanSuggest(_player))
                            {
                                Suggest frm = new Suggest(this);
                                frm.ShowDialog();
                                ws.ChangeTurn(_gameID);
                                _player = (Player)ws.GetCharacter(_gameID, _player.PlayerID);
                            }
                            else
                            {
                                ws.ChangeTurn(_gameID);
                                _player = (Player)ws.GetCharacter(_gameID, _player.PlayerID);
                            }
                            lblStatus.Text = "Waiting for other players to make their moves.";
                            // Start timer
                            timerGame.Start();
                        }
                    }
                    else
                    {
                        MessageBox.Show("Sorry, this is not your turn.", "Not your turn...", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    }
                }
            }
        }

        private void btnAccuse_Click(object sender, EventArgs e)
        {
            Accuse frm = new Accuse(this);
            frm.ShowDialog();
            // Start timer
            timerGame.Start();
        }

        private void cards_Click(object sender, EventArgs e)
        {
            if (sender != null)
            {
                PictureBox pb = (PictureBox)sender;

                if (pb.Cursor ==  Cursors.No)
                    return;

                Card card = (Card)pb.Tag;

                if (_rules.CanDisprove(_player, _incomingSuggestion))
                {
                    ws.Disprove(_gameID, _player.PlayerID, _incomingSuggestion.SuggestionID, false, card);
                    // Start timer
                    timerGame.Start();
                }
            }
        }

        private void cards_MouseHover(object sender, EventArgs e)
        {
            if (sender != null && sender is PictureBox)
            {
                PictureBox pb = (PictureBox)sender;
                if (pb.Tag == null)
                {
                    pb.Cursor = Cursors.No;
                    return;
                }
                Card card = (Card)pb.Tag;
                if (_rules.CanDisprove(_player, _incomingSuggestion, card))
                {
                    pb.Cursor = Cursors.Hand;
                }
                else
                {
                    pb.Cursor = Cursors.No;
                }
            }
        }
        
        /// <summary>
        /// On closing event. Stores game and player ID
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_gameStatus == GameStatus.Finished)
            {
                //do nothing
            } 
            else if (MessageBox.Show("Are you sure you would like to temporarily leave the game? If you are ready to give up, please select Quit Game instead.", "Exit", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation) == DialogResult.Yes)
            {
                if (_player != null)
                {
                    Properties.Settings.Default.PlayerID = _player.PlayerID;
                }
                Properties.Settings.Default.GameID = _gameID;
                Properties.Settings.Default.Save();
            }
            else
            {
                e.Cancel = true;
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Showing suggestion log
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            SuggestionLog frm = new SuggestionLog(_gameID);
            frm.Show();
        }

        /// <summary>
        /// User key press
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control &&
                e.KeyCode == Keys.F1 &&
                _characters[0] is Player)
            {
                _player.PlayerID = _characters[0].PlayerID;
                DrawCards();
                // Start timer
                timerGame.Start();
            }
            if (e.Control &&
                e.KeyCode == Keys.F2 &&
                _characters[1] is Player)
            {
                _player.PlayerID = _characters[1].PlayerID;
                DrawCards();
                // Start timer
                timerGame.Start();
            }
            if (e.Control &&
                e.KeyCode == Keys.F3 &&
                _characters[2] is Player)
            {
                _player.PlayerID = _characters[2].PlayerID;
                DrawCards();
                // Start timer
                timerGame.Start();
            }
            if (e.Control &&
                e.KeyCode == Keys.F4 &&
                _characters[3] is Player)
            {
                _player.PlayerID = _characters[3].PlayerID;
                DrawCards();
                // Start timer
                timerGame.Start();
            }

            if (e.Control && e.Alt &&
                e.KeyCode == Keys.E)
            {
                Envelope env = ws.GetEnvelope(_gameID);
                MessageBox.Show("You've pressed a secret key combination to cheat and open an envelope." +
                   "\nRoom: " + env.SuspectRoom.Name + "\nWeapon: " + env.SuspectWeapon.Name + "\nCharacter: " + env.SuspectCharacter.Name);
            }
        }
        /// <summary>
        /// User skips move if no moves are available
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSkip_Click(object sender, EventArgs e)
        {
            ws.SkipTurn(_gameID, _player.PlayerID);
            btnSkip.Enabled = false;
            lblStatus.Text = "Waiting for other players to make their moves.";

            // Start timer
            timerGame.Start();
        }

        private void quitGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Are you sure you would like to quit the game? If you do so, you'll lose the game.", "Exit", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation) == DialogResult.Yes)
            {
                ws.QuitGame(_gameID, _player.PlayerID);
                timerChat.Stop();
                timerGame.Stop();
                _gameStatus = GameStatus.Finished;
                Properties.Settings.Default.PlayerID = -1;
                Properties.Settings.Default.GameID = -1;
                Properties.Settings.Default.Save();
                Close();
            }
        }

        private void userGuideToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Help.ShowHelp(lblStatus, "file://" + Environment.CurrentDirectory + "\\clueless_help.chm", HelpNavigator.Index);
        }
        #endregion
               
    }
}